package p14_cglib的MethodProxy原理;

import org.springframework.cglib.core.Signature;
import p13_cglib代理原理.Target;

/**
 * MethodProxy在被创建时，底层cglib会扫描目标类，并用字节码技术动态生成一个对象FastClass，本质也是代理类
 * 该对象是结合目标类使用的
 * 该对象的结构大致分为:
 *  1.目标类所有公共方法的签名(方法名，and参数,返回值的描述符)
 *  2.getIndex方法，根据传进来的签名参数返回一个编号
 *  3.invoke方法，根据序号直接调用目标方法
 *
 * MethodProxy m = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper")
 * 创建MethodProxy时，就知道了其对应的参数,返回值的描述符("save", "()V")
 * 这个代理类的方法，全是运行时扫描目标类的结构，动态生成的
 */
public class _1_TargetFastClass {
    //下面的Signature是根据目标类的扫描结构，动态生成的
    static Signature s0 = new Signature("save", "()V");
    static Signature s1 = new Signature("save", "(I)V");
    static Signature s2 = new Signature("save", "(J)V");

    /**
     * 下面根据传入的签名，返回方法编号，也是动态生成的
     * methodProxy.invoke(t, arg)
     * 上面这行代码的methodProxy对象就对应一个唯一的Signature
     * @param s
     * @return
     */
    public int getIndex(Signature s) {
        if (s0.equals(s)) {
            return 0;
        } else if (s1.equals(s)) {
            return 1;
        } else if (s2.equals(s)) {
            return 2;
        }
        return -1;
    }

    /**
     * methodProxy.invoke(t, arg)这行代码就会间接调用FastClass的invoke方法
     * index可以根据methodProxy获得，target和args参数又是传进来的，故而可以正常调用
     * @param index 要调用的方法的编号
     * @param target   目标类对象
     * @param args  目标方法的参数
     * @return
     */
    public Object invoke(int index, Object target, Object[] args) {
        if(index == 0) {
            ((Target) target).save();
            return null;
        } else if (index == 1) {
            ((Target) target).save(((int) args[0]));
            return null;
        } else if (index == 2) {
            ((Target) target).save(((long) args[0]));
            return null;
        }
        throw new NoSuchMethodError();
    }

    public static void main(String[] args) {
        //首次使用MethodProxy的方法，就会动态创建fastClass
        _1_TargetFastClass fastClass = new _1_TargetFastClass();
        //MethodProxy根据自己方法签名，从fastClass得到一个方法编号，并记下自己的编号
        int index = fastClass.getIndex(new Signature("save", "(J)V"));
        //MethodProxy调用invoke方法时，会间接调用fastClass的invoke
        fastClass.invoke(index, new Target(), new Object[]{1L});
        /**
         * 以上步骤，就是MethodProxy直接调用目标方法的大致流程
         * TargetFastClass是结合目标类的方法使用的，结合代理方法使用的FastClass也是类似
         */
    }
}
